package game;

import java.awt.Graphics;
import java.awt.Rectangle;
import java.io.File;
import java.io.IOException;

import system.*;

public class Scroller extends Scene{

	private Boss boss;
	private Player player;
	private Interface hud;
	private Director asteroidDirector, bulletDirector, coinDirector, enemyBulletDirector, particleDirector, powerupDirector;
	private TextDirector damageDirector;
	private TextHandler messageHandler;

	Rectangle r1; // The player's bounding box

	int spawnCounter; // Ticks every cycle; spawns asteroid if spawn_count is reached
	int coinSpawnCounter;
	int currentLevel = 1;
	int deathCountdown = 60;

	boolean doCollisions = true;	// do we do collisions this update()?
	boolean damaged = false;		// is the hero damaged?
	boolean bossmode = false;
	public boolean bosskilled = false;	

	private int spawnAsteroid = 10; // How often should we spawn an asteroid?
	private int spawnCoin = 500; // How often should we spawn an asteroid?

	public Scroller(Game aGame, InputManager IM, SoundLibrary jukebox, int level) {
		super(aGame, IM, jukebox);

		asteroidDirector = new Director();
		bulletDirector = new Director();
		enemyBulletDirector = new Director();
		coinDirector = new Director();
		particleDirector = new Director();
		powerupDirector = new Director();
		damageDirector = new TextDirector();

		messageHandler = new TextHandler(200, 130, "top", 60, 0, true, 14, 14);
		messageHandler.makeAlive(true);
		messageHandler.setText("LEVEL " + Integer.toString(currentLevel));
		messageHandler.requireUpdate(true);
		messageHandler.update();

		currentLevel = level;

		background = new Background(6);
		hud = new Interface(new TextHandler(385, 255, "score", -1, 0, false));		

		spawnCounter = 0;
		coinSpawnCounter = 0;

		updateSpawnConstants();

		try {
			player = new Player(new AnimatedSprite(new File("assets/sprites/ship.png"),35,19,true, 90), 50, 137);
		} catch (IOException e) {
			System.out.println("Could not load ship sprite.");
		}

	}

	private void updateSpawnConstants() { // super arbitrary
		spawnAsteroid = (int)(spawnAsteroid * 0.8);
		spawnCoin = 500;
	}

	public float getPlayerX(){
		return player.getX();
	}

	public float getPlayerY(){
		return player.getY();
	}

	@Override
	public void paint(Graphics g){


		// Draw the Background
		for(int i=0; i<background.getLength(); i++){
			background.getActor(i).update();
			g.drawImage(background.getActor(i).getImage(), background.getActor(i).getX(), background.getActor(i).getY(), getGame());
		}

		// Draw the Directors
		for(int i=0; i<asteroidDirector.getSize(); i++){
			g.drawImage(asteroidDirector.getActor(i).getImage(), asteroidDirector.getActor(i).getX(), asteroidDirector.getActor(i).getY(), getGame());
		}
		for(int i=0; i<bulletDirector.getSize(); i++){
			g.drawImage(bulletDirector.getActor(i).getImage(), bulletDirector.getActor(i).getX(), bulletDirector.getActor(i).getY(), getGame());
		}
		for(int i=0; i<coinDirector.getSize(); i++){
			g.drawImage(coinDirector.getActor(i).getImage(), coinDirector.getActor(i).getX(), coinDirector.getActor(i).getY(), getGame());
		}

		// Draw the Boss
		if(bossmode){
			if(boss != null){
				g.drawImage(boss.getImage(), boss.getX(), boss.getY(), getGame());
			}
		}
		for(int i=0; i<enemyBulletDirector.getSize(); i++){
			g.drawImage(enemyBulletDirector.getActor(i).getImage(), enemyBulletDirector.getActor(i).getX(), enemyBulletDirector.getActor(i).getY(), getGame());
		}

		for(int i=0; i<particleDirector.getSize(); i++){
			g.drawImage(particleDirector.getActor(i).getImage(), particleDirector.getActor(i).getX(), particleDirector.getActor(i).getY(), getGame());
		}
		for(int i=0; i<powerupDirector.getSize(); i++){
			g.drawImage(powerupDirector.getActor(i).getImage(), powerupDirector.getActor(i).getX(), powerupDirector.getActor(i).getY(), getGame());
		}		


		// Draw the Player
		g.drawImage(player.getImage(), player.getX(), player.getY(), getGame());



		// Draw any Text
		for(int i=0; i < damageDirector.getLength(); i++){
			try {
				if( !damageDirector.isEmpty() ){
					for(int j = 0; j < 10; j++){

						int imageX = damageDirector.getHandler(i).getX()+damageDirector.getHandler(i).getTextAt(j).getX();
						int imageY = damageDirector.getHandler(i).getY()+damageDirector.getHandler(i).getTextAt(j).getY();

						g.drawImage(damageDirector.getHandler(i).getTextAt(j).getImage(), imageX, imageY, getGame());
					}
				}
			} catch (Exception e) {
				System.out.println("unable to draw damage text at index: " + i); 
				e.printStackTrace();
				System.exit(0);
			}

		}
		if(messageHandler.isAlive()){
			for(int j = 0; j < 10; j++){
				g.drawImage(messageHandler.getTextAt(j).getImage(), messageHandler.getTextAt(j).getX(), messageHandler.getTextAt(j).getY(), getGame());
			}
		}

		// Draw the HUD
		g.drawImage(hud.getFrame(), 0, (270-20), getGame());
		g.drawImage(hud.getHPBar(), hud.hp.getX(), hud.hp.getY(), getGame());
		for(int i=0; i<10; i++){
			g.drawImage(hud.getScoreTextHandler().getTextAt(i).getImage(), hud.getScoreTextHandler().getTextAt(i).getX(), hud.getScoreTextHandler().getTextAt(i).getY(), getGame());
		}
	}

	@Override
	public synchronized void update(){
		player.setHp(100); // god mode
		player.update(inputs);
		asteroidDirector.update();
		bulletDirector.update();
		enemyBulletDirector.update();
		coinDirector.update();
		particleDirector.update();
		powerupDirector.update();
		damageDirector.update();

		hud.update();
		if(messageHandler.isAlive()){
			messageHandler.requireUpdate(true);
			messageHandler.update();
		}

		if(!bossmode){
			// Update asteroid spawn
			spawnCounter++;
			if(spawnCounter >= spawnAsteroid){
				asteroidDirector.addAsteroid();
				spawnCounter = 0;
			}

			// update coin spawn
			coinSpawnCounter++;
			if(coinSpawnCounter >= spawnCoin){
				coinDirector.addCoin();
				coinSpawnCounter = 0;
			}
		}

		// check to see if we should fire off a bullet
		// conditions: firebullet flag is set and it's either (bossmode and the boss isn't in intro state || it's just not boss mode)
		if (player.getFireBullet() && ((bossmode && boss.getState() != boss.STATE_INTRO) || (!bossmode))){
			jukebox.Play("shoot.wav");
			for(int i=0; i<15; i++){
				bulletDirector.addBullet(player, 15, (float)(-5 + i*0.5));
			}
			//bulletDirector.addBullet(player, 15, 0);

			player.setFireBullet(false);
		}

		if(hud.getScore() >= 1000 * Math.pow(currentLevel,2)){
			if(!bossmode){
				bossmode = true;
				bosskilled = false;

				try {
					boss = new Boss(this, enemyBulletDirector, new AnimatedSprite(new File("assets/sprites/BOSSSTAND.png"),108,121,true, 30), (int)(currentLevel/2 + 3.5));
				} catch (IOException e) {
					System.out.println("Can't find boss sprite");
					System.exit(0);
				}
			}
			if(bossmode){
				boss.update();
			}
			if(bossmode && bosskilled){
				currentLevel++;
				updateSpawnConstants();

				bossmode = false;
				bosskilled = false;

				String theMessage = "LEVEL " + Integer.toString(currentLevel);

				messageHandler = new TextHandler(200, 130, "top", 60, 0, true, 14, 14);
				messageHandler.makeAlive(true);
				messageHandler.setText(theMessage);
			}

		}

		// Player death handler
		if(player.getHp() <= 0){

			deathCountdown--;
			if(deathCountdown <=0 ){
				ScoreHandler.writeScore(((Gigatron) game).getPlayerName(), Integer.toString(hud.getScore()));
				game.setState(Game.GAME_OVER);
			}
		}

		damaged = false;

		/************************************************************
		 *  COLLISION HANDLERS START NOW							*
		 ************************************************************/

		doCollisions = !doCollisions;
		if (doCollisions){
			// check for player-asteroid collisions
			r1 = player.getBounds();
			if(player.getState() == 4){
				r1 = new Rectangle();
			}
			for (int j = 0; j < asteroidDirector.getSize(); j++){
				Actor a = (Actor) asteroidDirector.getActor(j);
				Rectangle r2 = a.getBounds();

				if (r1.intersects(r2)){
					// damage asteroid
					jukebox.Play("break.wav");
					int power = 500;
					asteroidDirector.dealDamage(j, power);

					// damage player
					int power2 = a.getPower()*3;
					damageDirector.add(power2, "damage", asteroidDirector.getActor(j).getX()-25, asteroidDirector.getActor(j).getY() + 5, -1);
					particleDirector.addParticle(asteroidDirector.getActor(j).getX(), asteroidDirector.getActor(j).getY(), asteroidDirector.EXPLOSION_LG);

					int newHp = player.getHp() - power2;
					player.setHp(Math.max(newHp, 0));

					hud.setHPBar(newHp);

					if (player.getHp() <= 0){

						try {
							AnimatedSprite dying = new AnimatedSprite(new File("assets/sprites/ship-die.png"), 189, 184, false, -1);
							player.setSprite(dying);
						} catch (IOException e) {
							System.out.println("Can't load explosion animation");
						}
					}
				}
			}

			// check for player-coin collisions
			for (int j = 0; j < coinDirector.getSize(); j++){
				Actor a = (Actor) coinDirector.getActor(j);
				Rectangle r2 = a.getBounds();
				if (r1.intersects(r2)) {
					jukebox.Play("coin.wav");
					hud.addScore(500);
					particleDirector.addParticle(player.getX(), player.getY(), particleDirector.SPARKLE);
					coinDirector.remove(j);

				}
			}

			// check for player-powerup collisions
			for (int j = 0; j < powerupDirector.getSize(); j++){

				PowerUp thePowerUp = (PowerUp) powerupDirector.getActor(j);
				Rectangle r2 = thePowerUp.getBounds();

				if (r1.intersects(r2) && !thePowerUp.getAbsorbedState()) {
					jukebox.Play("coin.wav");

					switch(thePowerUp.getState()){
					case 1: {
						int newHp = player.getHp() + thePowerUp.getPower();
						player.setHp(Math.min(100, newHp));
						hud.setHPBar(newHp);
						break;
					}
					case 2: {
						player.setPower(player.getPower() + thePowerUp.getPower());
					}

					}
					thePowerUp.isAbsorbed();
					particleDirector.addParticle(player.getX(), player.getY(), particleDirector.SPARKLE);


				}
			}

			// check for bullet collisions
			for (int i = 0; i < bulletDirector.getSize(); i++) {
				Actor b = bulletDirector.getActor(i);
				Rectangle bulletBound = b.getBounds();
				for (int j = 0; j < asteroidDirector.getSize(); j++) { // asteroids
					if (bulletBound.intersects(asteroidDirector.getActor(j).getBounds())){

						// deal damage, remove small bullet, etc
						jukebox.Play("collide.wav");
						int power = (int) (b.getPower() * (Math.random() / 5 + 0.9));
						asteroidDirector.dealDamage(j, power);
						damageDirector.add(power, "damage", asteroidDirector.getActor(j).getX()+15, asteroidDirector.getActor(j).getY() + 5, 1);
						particleDirector.addParticle(bulletDirector.getActor(i).getX(), bulletDirector.getActor(i).getY(), particleDirector.EXPLOSION_SM);
						bulletDirector.remove(i);

						// handle case where asteroid gets destroyed
						if(asteroidDirector.getActor(j).getHp() <= 0){
							jukebox.Play("break.wav");
							hud.addScore(100);
							particleDirector.addParticle(asteroidDirector.getActor(j).getX(), asteroidDirector.getActor(j).getY(), particleDirector.EXPLOSION_LG);

							// 10% chance to make a powerup appear
							int chance = (int)Math.ceil(Math.random() * 100);
							if(chance <= 40){
								powerupDirector.addPowerup(player, asteroidDirector.getActor(j).getX(), asteroidDirector.getActor(j).getY());
							}
						}
						break;
					}
				}

				if(bossmode && bulletBound.intersects(boss.getBounds())){ // boss
					// deal damage, remove small bullet, etc
					jukebox.Play("collide.wav");
					int power = (int) (b.getPower() * (Math.random() / 5 + 0.9));
					boss.setHp(boss.getHp() - power);
					damageDirector.add(power, "damage", b.getX(), b.getY(), -1);
					particleDirector.addParticle(b.getX(), b.getY(), particleDirector.EXPLOSION_SM);
					bulletDirector.remove(i);

					// handle case where boss is killed
					if(boss.getHp() <= 0){
						jukebox.Play("break.wav");
						particleDirector.addParticle(b.getX(), b.getY()-10, particleDirector.EXPLOSION_LG);
						particleDirector.addParticle(b.getX()+10, b.getY()+10, particleDirector.EXPLOSION_LG);
						particleDirector.addParticle(b.getX()+20, b.getY(), particleDirector.EXPLOSION_LG);
					}
				}
			}

		}
	}




}
